#!/bin/bash
set -e
set -o nounset

#Colors
bold="`tput bold`" 
nobold="`tput sgr0`"
ul="`tput smul`"
noul="`tput rmul`"
red="`tput setaf 1`"
green="`tput setaf 2`"
yellow="`tput setaf 3`"
blue="`tput setaf 4`"
magenta="`tput setaf 5`"
cyan="`tput setaf 6`"
white="`tput setaf 7`"
nocolor="`tput setaf 9`"

#Setup default values and load .homecfg
CFG="$HOME/.homecfg"
VER="0.31"
[[ -f "$CFG" ]] && source "$CFG" 


USERDIR=${userdir:-.`whoami`}
[[ ! -e "$USERDIR/etc" ]] && pushd "$HOME" > /dev/null && mkdir -p $USERDIR/etc && popd > /dev/null
SCPFLAGS=${scpoptions:-""}
SCPREMOTE=${scpremote:-""}
USER=`whoami`

#Configuration archive
CFGARCHIVE=${archive:-"$USER-config.tgz"}
EXCLUDE=${exclude:-""}

PATH=${PATH}:~"/$USERDIR/sbin":`dirname $0`

#Display header with program name and set default color for output
progname()
{
   echo $bold"homecfg v$VER - Backup and restore personal configuration files"$nobold
   echo $green
}

#Display program options and help
help()
{
   echo "Usage: homecfg -r|-s| [-nhmd] [-f ${ul}file${noul}]"
   echo "       homecfg -a ${ul}file${noul}"
   echo "       homecfg -l ${ul}file${noul}"
   echo
   printf "   $green-s\t\t$nocolor Store configuration on the network\n"
   printf "   \t\t (and in ~/$USERDIR/etc/$CFGARCHIVE also).\n"
   printf "   $green-r\t\t$nocolor Restore configuration from the network.\n"
   printf "   $green-n\t\t$nocolor Do not copy configuration to/from the network, leave it only in local file.\n"
   printf "   $green-f ${ul}file\t${noul}${nocolor} Restore config from specified file.\n"
   printf "   $green-m\t\t$nocolor Add month/year suffix to configuration archive.\n"
   printf "   \t\t Useful to make monthly snapshot.\n"
   printf "   $green-d\t\t$nocolor Add year/month/day suffix to configuration archive.\n"
   printf "   \t\t Useful to make day snapshot.\n"
   printf "   $green-a ${ul}file\t${noul}${nocolor} Add file to the list of managed configuration files.\n"
   printf "   \t\t file must start with a . and be located in the user's HOME directory.\n"
   printf "   $green-l\t\t$nocolor List dot files managed in the HOME directory.\n"
   printf "   $green-h\t\t$nocolor this help.\n"
   echo
   echo "Examples:"
   echo "   Add .bashrc to list of managed configuration files:"
   echo "   $ homecfg -a ~/.bashrc"
   echo "   Store configuration archive in ~/$USERDIR/etc, but also on the network:"
   echo "   $ homecfg -s"
   echo "   Store configuration archive in ~/$USERDIR/etc (not on network):"
   echo "   $ homecfg -n"
   echo "   Restore configuration from network storage and make appropriate symbolic links:"
   echo "   $ homecfg -r"
}

#Get the source link for a specified managed configuration file (or directory)
#e.g. return .mike/_bashrc for .bashrc
getsrcformanaged()
{
   #Make sure file exists
   file=`basename "$1"`
   [[ -e "$HOME/$file" ]]  || { echo $red"ERROR: Managed configuration file $HOME/$1 does not exist."$nocolor && exit 1; }

   #Make sure file starts with a period
   [[ "$file" != .* ]] && echo $red"ERROR: Managed configuration files must start with a period."$nocolor && exit 1;

   #Return source link source with directory relative to HOME
   linkfile="${file/#./_}"
   result="$USERDIR/$linkfile"
}

getmanagedforsrc()
{
   managed=`basename "$1"`
   managed="${managed/#_/.}"
   result="$managed"
}

listmanaged()
{
   i=0
   for f in "$HOME/$USERDIR"/_*; do
      if [[ -e "$f" ]]; then
         getmanagedforsrc "$f"
         managed="$result"
         getsrcformanaged "$managed"
         src="$result"
         printf "$green~/$src\t-->\t$blue~/$managed$nocolor\n"
         let ++i
      fi
   done
   set +o nounset
   if [[ $1 != -q ]]; then
      echo "$i managed links."
      echo "Nothing has been changed."
   fi
   set -o nounset
}

addmanaged()
{
   #Get link target name (e.g. .ologgio/_homecfg)
   file=`basename "$1"`
   getsrcformanaged "$file"
   src="$result"
   echo "Adding $file to managed links."

   #Exit if file is already managed 
   #(i.e. if it is a symbloc link that points to $USERDIR/_file)
   pushd "$HOME" > /dev/null
   [[ -L "$file" && "`readlink "$file"`" == $src ]] && { echo $red"ERROR: $file is already managed." && exit 1; }

   #Otherwise, copy the file to the _file and create the symbolic link
   echo -n "Copying..."
   cp -v $file $src 
   rm  $file
   echo -n "Linking..."
   ln -vs $src $file
   popd > /dev/null
}

#Tar, compress and save my personal config
#optionally copy it to the network using scp
save()
{
   echo "Taring and compresing personal config..."
   #set xopt to the --exclude-from=$EXCLUDE if the configuration
   #variable (and file) exist
   xopt=""
   if [[ -n "$EXCLUDE" ]]; then
      [[ -f "$HOME/$EXCLUDE" ]] || { echo $red"ERROR: Exclude file $EXCLUDE does not exist." && exit 2; }
      xopt="--exclude-from=$EXCLUDE";
   fi

   #Tar the configuration and copy it to the network with scp 
   #unless the user passed the -n option
   pushd "$HOME" > /dev/null
   tar --exclude-backups --exclude-vcs --exclude="*~" --exclude="*${CFGARCHIVE::-4}*" $xopt -zcvf "/tmp/$CFGARCHIVE" "$USERDIR" | sed -e "s/^/  >>> /g" 
   echo $nocolor
   mv --backup=simple --suffix=.bak "/tmp/$CFGARCHIVE" "$HOME/$USERDIR/etc/"
   popd > /dev/null

   extramsg=""
   if type -t scp > /dev/null && [[ "$COPY2NET" == "yes" ]]; then
      if [ netup ]; then
         echo $green"Copying $CFGARCHIVE to network..."
         echo $green"* If you see an error like:"
         echo $green"    \"stdin: not a tty\", just ignore it. It's not really a problem."
         echo $green"* We move files to the network with scp, so you may be asked for"
         echo $green"  a passphrase."
         echo $nocolor
         set +e
         scp $SCPFLAGS "$HOME/$USERDIR/etc/$CFGARCHIVE" ${SCPREMOTE}
         if [ $? == 0 ]; then
            echo $green"Configuration copied to network."$nocolor;
            extramsg="and in '$SCPREMOTE'"
         else
            echo $red"ERROR: Copying configuration to network."$nocolor
            exit 1
         fi
         set -e
      else
         echo $red"WARNING: Network down, skipping copy to network."
      fi
   fi
   echo "These files will be symbolic links if the configuration is restored:"
   listmanaged -q
   echo "Configuration archive in ~/$USERDIR/etc/$CFGARCHIVE $extramsg."
}

#Restore config from network or file. File is in $1.
#If $1 is empty get configuration archive from network
#via scp.
restore()
{
   #Get configuration file from network if $1 is was not passed
   if [[ $# -lt 1 && "$COPY2NET" == "yes" ]]; then
      echo "Restoring configuration from network..."
      if ! netup; then
         echo $red"ERROR: Network is down."
         exit 1
      fi
      if ! type -t scp > /dev/null; then
         echo $red"ERROR: scp not installed, can't restore from network."
         exit 1
      fi

      echo $green"Reading $CFGARCHIVE from network..."
      echo $green"* If you see an error like:"
      echo $green"    \"stdin: not a tty\", just ignore it. It's not really a problem."
      echo $green"* We move files to the network with scp, so you may be asked for"
      echo $green"  a passphrase."
      echo $nocolor
      [[ -f "$HOME/$USERDIR/etc/$CFGARCHIVE" ]] &&  mv "$HOME/$USERDIR/etc/$CFGARCHIVE"{,.bak} 
      set +e
      scp $SCPFLAGS ${SCPREMOTE}"$CFGARCHIVE" "/tmp/$CFGARCHIVE" 
      if [[ $? != 0 ]]; then
         echo $red"ERROR: Reading configuration from network."
         exit 1
      fi
      set -e
      cfgfile="/tmp/$CFGARCHIVE" 
   elif [[ $COPY2NET=no ]]; then
      #File is local, from .homecfg or default
      echo "Restoring configuration from local file..."
      cfgfile="$HOME/$USERDIR/etc/$CFGARCHIVE"
   else
      #Filename passed by user -f flag
      cfgfile="$1"
   fi
  
   #Untar configuration file and restore symbolic links 
   [[ -e "$cfgfile" ]] || { echo $red"ERROR: $cfgfile does not exist.$nocolor" && exit 1; }
   echo $green"Restoring configuration from $cfgfile..."
   tar -C "$HOME" -zxvf "$cfgfile" | sed -e "s/^/  <<< /g" 
   echo $green"Recreating symbolic links."$nocolor
   for f in "$HOME/$USERDIR"/_*; do
      if [[ -e "$f" ]]; then
         src=`basename "$f"`
         link="${src/#_/.}"
         #If there is a symbolic link already with that name
         # then replace it.
         pushd "$HOME" > /dev/null || { echo $red"ERROR: Chaning to home directory." && exit 1; }
         if [[ -L "$link" ]]; then
            echo "   Replacing existing link: ~/$link --> ${green}~/$USERDIR/$src"$nocolor
            ln -sf "$USERDIR/$src" "$link" 
         else
            echo "   Creating new link: ~/$link --> ${green}~/$USERDIR/$src"$nocolor
            ln -s "$USERDIR/$src" "$link" 
         fi
         popd > /dev/null
      fi
   done
   echo $nocolor
}

#Print program name
progname

#Command-line paramenters
COPY2NET="yes"
ARGS=""
action=""
while getopts "lmdnhrsf:a:" opt; do
   case "$opt" in
      h) help && exit 0
         ;;
      n) COPY2NET="no"
         ;;
      m) CFGARCHIVE="$USER-config-`date +%b%Y`.tgz"
         ;;
      d) CFGARCHIVE="$USER-config-`date +%Y%m%d`.tgz"
         ;;
      l) action="listmanaged"
         ;;
      a) action="addmanaged"
         ARGS="$OPTARG"
         ;;
      r) action="restore"
         ;;
      s) action="save"
         ;;
      f) action="restore";
         CFGARCHIVE="$OPTARG"
         ;;
      [?]) help; echo $red"ERROR: unknown option."; exit 0
         ;;
   esac
done
[[ -z $action ]] && help && echo $red"ERROR: Either -r,-s,-a or -l must be passed"$nocolor
$action $ARGS
echo $green"Done."
echo $blue"Have a good day!!"$nocolor

